package com.terrynoya.fight.scriptEngine
{
	import com.terrynoya.fight.evaluation.Node;
	

	public class DescRecParser
	{
		private var _index:int;
		
		private var _tokens:Array;
		
		public function DescRecParser()
		{
			
		}
		
		/**
		 * stmt			-> 	if-stmt | exp
		 * if-stmt 		-> 	if(exp) stmt else stmt
		 * exp 			-> 	logicExp [logicOp logicExp]
		 * logicExp		->  simpleExp [compareOp simpleExp]
		 * simpleExp	-> 	term [addOp term]
		 * term			->	factor [mulOp factor]
		 * factor		->	(simpleExp) | number
		 * addOp		-> 	+ | -
		 * mulOp		->	* | / | %
		 * compareOp	->  > | >= | < | <= | == | !=
		 * logicOp		->	&& | ||
		 * @param tokens
		 * @return 
		 * 
		 */		
		public function parse(tokens:Array):TokenNode
		{
			this._tokens = tokens;
			var node:TokenNode = this.statement();
			return node;
		}
		
		private function statement():TokenNode
		{
			var node:TokenNode = null;
			
			var token:TokenVO = this.getToken();
			if(token == null)
			{
				return null;
			}
			else if(token.value == KeyWord.IF)
			{
				node = this.ifStatement();			
			}
			else
			{
				node = this.exp();			
			}
			return node;
		}
		
		private function ifStatement():TokenNode
		{
			var node:TokenNode = new TokenNode(TokenType.IF,this.getToken());
			this.match(TokenVO.IF);
			this.match(TokenVO.LEFT_BRACKET);
			node.addChild(this.exp());
			this.match(TokenVO.RIGHT_BRACKET);
			node.addChild(this.statement());
			this.match(TokenVO.ELSE);
			node.addChild(this.statement());
			return node;
		}
		
		private function exp():TokenNode
		{
			var rlt:TokenNode = null;
			var left:TokenNode = this.logicExp();
			rlt = left;
			while(this.getToken() != null && this.getToken().type == TokenVO.LOGIC_OP)
			{
				var opNode:TokenNode = new TokenNode(TokenType.LOGIC_OPER,this.getToken());
				this.match(TokenVO.LOGIC_OP);
				rlt = opNode;
				var rightNode:TokenNode = this.logicExp();
				opNode.addChild(left);
				opNode.addChild(rightNode);
			}
			return rlt;
		}
		
		private function logicExp():TokenNode
		{
			var rlt:TokenNode = null;
			var left:TokenNode = this.simpleExp();
			rlt = left;
			while(this.getToken() != null && this.getToken().type == TokenVO.COMPAR_OP)
			{
				var opNode:TokenNode = new TokenNode(TokenType.COMPARE_OPER,this.getToken());
				rlt = opNode;
				this.match(TokenVO.COMPAR_OP);
				var right:TokenNode = this.simpleExp();
				rlt.addChild(left);
				rlt.addChild(right);
			}
			return rlt;
		}
		
		private function simpleExp():TokenNode
		{
			var rlt:TokenNode = this.term();
			while(this.getToken() != null && this.getToken().type == TokenVO.ADD_OPER)
			{
				var opNode:TokenNode = new TokenNode(TokenType.OPER,this.getToken());
				opNode.addChild(rlt);
				rlt = opNode;
				this.match(TokenVO.ADD_OPER);
				var right:TokenNode = this.term();
				opNode.addChild(right);
			}
			return rlt;
		}
		
		private function term():TokenNode
		{
			var rlt:TokenNode = this.factor();
			while(this.getToken() != null && this.getToken().type == TokenVO.MUTI_OPER)
			{
				var opNode:TokenNode = new TokenNode(TokenType.OPER,this.getToken());
				opNode.addChild(rlt);
				rlt = opNode;
				this.match(TokenVO.MUTI_OPER);
				var right:TokenNode = this.factor();
				opNode.addChild(right);
			}
			return rlt;
		}
		
		private function factor():TokenNode
		{
			var rlt:TokenNode = null;
			var token:TokenVO = this.getToken();
			switch(token.type)
			{
				case TokenVO.NUMBER:
				{
					rlt = new TokenNode(TokenType.CONST,token);
					this.match(TokenVO.NUMBER);
					break;
				}
				case TokenVO.LEFT_BRACKET:
				{
					this.match(TokenVO.LEFT_BRACKET);
					rlt = this.simpleExp();
					this.match(TokenVO.RIGHT_BRACKET);
					break;
				}
			}
			return rlt;
		}
		
		private function match(value:int):void
		{
			var token:TokenVO = this.getToken();
			if(token == null || token.type != value)
			{
				throw new Error("match error");
				return;
			}
			this._index ++;
		}
		
		private function getToken():TokenVO
		{
			return this._tokens[this._index];
		}
		
		public function print(node:TokenNode,depth:int = 0):void
		{
			trace(this.getIndent(depth) + node.value);
			for (var i:int = 0; i < node.chilrenLength; i++) 
			{
				this.print(node.getChildAt(i),depth + 1);
			}
		}
		
		private function getIndent(depth:int):String
		{
			var tmpStr:String = "--";
			var rlt:String = "";
			for (var i:int = 0; i < depth; i++) 
			{
				rlt += tmpStr;
			}
			return rlt;
		}
	}
}